/**
 * @file caculator.H
 * @brief
 * @author htli (22235058@zju.edu.cn)
 * @version 1.0
 * @date 2022-11-10
 *
 * @copyright Copyright (c) 2022  linhuo2020
 *
 */
#include <iostream>
#include <string>
#include <stack>
#include <vector>
#include <algorithm>
#include "BinaryOperator.H"
using namespace std;

class caculator
{
public:
    caculator(){};
    ~caculator() = default;
    bool solve(const string _infix, double &result);

private:
    bool infix2postfix(string &_infix, vector<string> &_postfix);
    bool solvePostfix(vector<string> &_postfix, double &_result);
};

bool caculator::solve(const string _infix, double &result)
{
    string infix;
    infix = _infix;
    vector<string> postfix;
    if (infix2postfix(infix, postfix))
        if (solvePostfix(postfix, result))
            return true;
    return false;
}

bool caculator::infix2postfix(string &_infix, vector<string> &_postfix)
{
    stack<char> Ops;
    _postfix.resize(0);
    string temp;
    /**
     * @brief 1. regularize the infix, delete all space and insert necessary zero.
     */
    _infix.erase(remove((_infix).begin(), _infix.end(), ' '), _infix.end());
    if (_infix.empty())
    {
        std::cerr << "Empty expression\n";
        return false;
    }
    for (auto i = _infix.begin(); i < _infix.end(); i++)
    {

        if (isNumber(*i) || isOpt(*i) || (*i) == '.')
            continue;
        else
        {
            cerr << "Illegal input char: " << *i << "\n";
            return false;
        }
    }
    if (OpPriorLevel(*_infix.begin()) == OpPriorLevel('+')) ///< 在表达式或括号开头的与'+'同级的符号前插入'0'.
        _infix.insert(0, 1, '0');
    for (auto i = _infix.begin(); i < _infix.end(); i++)
    {
        if (*i == '(')
            if (OpPriorLevel(*(i + 1)) == OpPriorLevel('+'))
                _infix.insert(i + 1, 1, '0');
    }
    /**
     * @brief 2. transform the regularized infix into postfix.
     */
    for (auto i = _infix.begin(); i < _infix.end(); i++)
    {
        if (isNumber(*i) || *i == '.') ///< 处理字符串中的数字.
        {
            temp = *i;
            while ((i + 1) < _infix.end() &&
                   (isNumber(*(i + 1)) || *(i + 1) == '.'))
            {
                temp += *(i + 1);
                i++;
            }
            if (count(temp.begin(), temp.end(), '.') > 1) ///< 正则性判断： 数字中不允许出现两个及以上的'.'
            {
                std::cerr << "Wrong number: " << temp << "\n";
                return false;
            }
            _postfix.push_back(temp);
        }
        else ///< 处理字符串中的运算符.
        {
            if (Ops.empty() || *i == '(')
                Ops.push(*i);
            else if (*i == ')')
            {
                while (Ops.top() != '(')
                {
                    temp = Ops.top();
                    _postfix.push_back(temp);
                    Ops.pop();
                    if (Ops.empty())
                    {
                        cerr << "Brackets are unmatched\n";
                        return false;
                    }
                }
                Ops.pop();
            }
            else
            {
                if (*i != '^') ///< 由于 '^' 是唯一的右结合运算, 且不存在比它更优先的运算, 在任何情况下 '^'都应该直接入栈.
                    while ((!Ops.empty()) && (OpPriorLevel(*i) <= OpPriorLevel(Ops.top())))
                    {
                        temp = Ops.top();
                        _postfix.push_back(temp);
                        Ops.pop();
                    };
                Ops.push(*i);
            }
        }
    }
    while (!Ops.empty())
    {
        temp = Ops.top();
        _postfix.push_back(temp);
        Ops.pop();
    }
    return true;
};

bool caculator::solvePostfix(vector<string> &_postfix, double &_result)
{
    stack<double> res;
    double LeftNum, RightNum;
    double temp;
    for (auto i = _postfix.begin(); i < _postfix.end(); i++)
    {
        if (isNumber(*i))
            res.push(stod(*i));
        else
        {
            if (res.size() < 2)
            {
                cerr << "Illegal expression\n";
                return false;
            }
            RightNum = res.top();
            res.pop();
            LeftNum = res.top();
            res.pop();
            if (!BinaryOpCompute(LeftNum, RightNum, *i, temp))
                return false;
            res.push(temp);
        }
    };

    if (res.size() > 1)
    {
        cerr << "Illegal expression\n";
        return false;
    }
    _result = res.top();
    return true;
};
